1 module websocket_connection; 2 import hip.api.net.server; 3 import hip.api.net.utils; 4 import std.stdio; 5 import core.sync.mutex; 6 import handy_httpd; 7 import hipengine_commands; 8 9 struct Connection 10 { 11 uint id; 12 WebSocketConnection socket; 13 } 14 public __gshared Connection*[] connections; 15 16 class HipremeEngineWebSocketServer: WebSocketMessageHandler 17 { 18 // ctx.server.stop(); // Calling stop will gracefully shutdown the server. 19 this() 20 { 21 wsMutex = new shared Mutex; 22 } 23 24 private uint wsID; 25 26 override void onConnectionEstablished(WebSocketConnection conn) 27 { 28 // synchronized(wsMutex) 29 { 30 Connection* c; 31 if(freeIdsList.length) 32 { 33 uint last = freeIdsList[$-1]; 34 freeIdsList = freeIdsList[0..$-1]; 35 c = connections[last]; 36 c.socket = conn; 37 writeln("Socket ID ", NetID.start + c.id, " reacquired"); 38 } 39 else 40 { 41 connections~= c = new Connection(id, conn); 42 wsID = id++; 43 } 44 uint data = NetID.start + c.id; 45 writeln("Socket ID ", data, " connected. Returning its ID."); 46 conn.sendBinaryMessage(toBytes(data)); 47 } 48 } 49 50 override void onTextMessage(WebSocketTextMessage msg) 51 { 52 import std.stdio; 53 import std.algorithm.searching; 54 55 string text = msg.payload; //Irrelevant here 56 57 // infoF!"Got TEXT: %s"(msg.payload); 58 } 59 60 static bool sendBinaryToSocket(uint toSocketID, ubyte[] data) 61 { 62 if(toSocketID >= connections.length) 63 { 64 writeln("toSocketID [", toSocketID, "] is out of range"); 65 return false; 66 } 67 68 connections[toSocketID].socket.sendBinaryMessage(data); 69 return true; 70 } 71 72 /** 73 * Specification: 74 * 4 bytes is the from socket ID 75 * 4 bytes is the to socket ID 76 * 77 * Remaining bytes are the data frame 78 * 79 * Params: 80 * msg = 81 */ 82 override void onBinaryMessage(WebSocketBinaryMessage msg) 83 { 84 import std.stdio; 85 86 uint fromSocketID = *cast(uint*)msg.payload[0..4] - NetID.start; 87 uint toSocketID = *cast(uint*)msg.payload[4..8]; 88 89 ubyte[] data = fromNetworkBytes(msg.payload[8..$])[NetHeader.sizeof..$]; 90 91 switch(toSocketID) 92 { 93 case NetID.server: 94 95 ubyte command = *cast(ubyte*)data.ptr; 96 writeln("Received command ID [", command, "] from connection ", fromSocketID, " Buffer is ", data); 97 switch(command) 98 { 99 case MarkedNetReservedTypes.connect: 100 //Just pong it back to say it has connected. 101 sendBinaryToSocket(fromSocketID, getNetworkFormattedData(MarkedNetReservedTypes.connect)); 102 break; 103 case MarkedNetReservedTypes.get_connected_clients: 104 auto response = getNetworkFormattedData(getConnectedClients(fromSocketID), MarkedNetReservedTypes.get_connected_clients); 105 sendBinaryToSocket(fromSocketID, response); 106 break; 107 case MarkedNetReservedTypes.client_connect: 108 uint targetID = *cast(uint*)(data.ptr+ubyte.sizeof); //After the command, the targetID 109 sendBinaryToSocket(targetID - NetID.start, getNetworkFormattedData(ConnectToClientResponse(fromSocketID), MarkedNetReservedTypes.client_connect)); 110 break; 111 default: 112 113 writeln("Invalid Command Received. Buffer: ", data); 114 break; 115 } 116 break; 117 case NetID.broadcast: 118 writeln(fromSocketID, " is broadcasting!"); 119 foreach(c; connections) 120 { 121 if(c.id != fromSocketID) 122 c.socket.sendBinaryMessage(data); 123 } 124 break; 125 default: 126 toSocketID-= NetID.start; 127 if(connections.length <= toSocketID) 128 writeln("Socket tried sending data to invalid socket ID ", toSocketID + NetID.start); 129 else 130 connections[toSocketID].socket.sendBinaryMessage(data); 131 break; 132 133 } 134 // writeln("Socket ", fromSocketID, " is sending ", data.length, " bytes to socket ", toSocketID); 135 136 } 137 138 override void onCloseMessage(WebSocketCloseMessage msg) 139 { 140 import std.algorithm.searching; 141 import std.conv:to; 142 synchronized 143 { 144 ptrdiff_t index = countUntil!((Connection* c) => c.socket == msg.conn)(connections); 145 if(index != -1) 146 { 147 writeln("Socket ", NetID.start + connections[index].id, " closed"); 148 connections[index].socket = null; 149 freeIdsList~= index.to!uint; 150 } 151 } 152 } 153 } 154 155 private __gshared uint id; 156 157 private __gshared uint[] freeIdsList; 158 private shared Mutex wsMutex;